﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Go;
using Network;

public class FogGoNetwork : MonoBehaviour
{
	FogGo game;
	public Player self;
	public int board_size = 19;
	public float board_line_thickness = 0.05f;
	public float start_circle_size = 0.2f;
	public float stone_size = 0.9f;
	Move candidateMove;
	

	// display
	[Range(0, 1)]
	public float fogThick = 0.5f;
	public Sprite whiteStoneSprite;
	public Sprite blackStoneSprite;
	public Sprite fogSprite;
	public Transform lastMoveHighLlight;
	SpriteRenderer[,] stoneRenders;
	SpriteRenderer[,] fogRenders;
	bool displayToggle;

	// multiplayer
	public string localIp;
	public int port = 5004;
	public string selfName;
	public string opponentName;
	BroadcastServer broadcastServer;
	BroadcastClient broadcastClient;
	INetwork network;
	bool hostStartToggle;
	float connectWatingTime;

	// UI
	public InputField nameInputField; GameObject nameInputFieldGo;
	public Button serverButton; GameObject serverButtonGo;
	public Button clientButton; GameObject clientButtonGo;
	public Button stopConnectButton; GameObject stopConnectButtonGo;
	public Button confirmButton; GameObject confirmButtonGo;
	public Button resignButton; GameObject resignButtonGo;
	public Button restartButton; GameObject restartButtonGo;
	public Image blackImage; GameObject blackImageGo;
	public Image whiteImage; GameObject whiteImageGo;
	public Text blackPlayerText;
	public Text whitePlayerText;
	public Text tipText;
	bool clearTipToggle;

	enum State
	{
		NotConnect,
		WaitingConnect,
		Playing,
		Gameover,
	}
	State curState;

	private void OnApplicationQuit()
	{
		if (broadcastServer != null)
		{
			broadcastServer.Close();
		}

		if (broadcastClient != null)
		{
			broadcastClient.Close();
		}

		if (network != null)
		{
			network.Close();
			if (network.IsHost())
			{
				Debug.Log("服务器断开连接");
			}
			else
			{
				Debug.Log("客户端断开连接");
			}
			network = null;
		}
	}

	// Start is called before the first frame update
	void Start()
    {
		Debug.Log("Start");

		InitDisplay();

		localIp = NetworkUtils.GetLocalIpAddress("InterNetwork")[0];

		nameInputFieldGo = nameInputField.gameObject;
		stopConnectButton.onClick.AddListener(() => { EndBroadcast(); });
		stopConnectButtonGo = stopConnectButton.gameObject;
		serverButton.onClick.AddListener(() => { SetAsHost(); });
		serverButtonGo = serverButton.gameObject;
		clientButton.onClick.AddListener(() => { FindingHost(); });
		clientButtonGo = clientButton.gameObject;
		confirmButton.onClick.AddListener(() => { ConfirmMove(); });
		confirmButtonGo = confirmButton.gameObject;
		resignButton.onClick.AddListener(() => { Resign(); });
		resignButtonGo = resignButton.gameObject;
		restartButton.onClick.AddListener(() => { RestartGame(); });
		restartButtonGo = restartButton.gameObject;
		blackImageGo = blackImage.gameObject;
		whiteImageGo = whiteImage.gameObject;

		curState = State.NotConnect;
	}

	// Update is called once per frame
	void Update()
    {
		if (!showingTip)
		{
			tipText.text = "";
		}
		switch (curState)
		{
			case State.NotConnect:
				serverButtonGo?.SetActive(true);
				clientButtonGo?.SetActive(true);
				stopConnectButtonGo?.SetActive(false);
				nameInputFieldGo?.SetActive(true);
				confirmButtonGo?.SetActive(false);
				resignButtonGo?.SetActive(false);
				restartButtonGo?.SetActive(false);
				blackPlayerText.text = "";
				whitePlayerText.text = "";
				break;
			case State.WaitingConnect:
				serverButtonGo.SetActive(false);
				clientButtonGo.SetActive(false);
				stopConnectButtonGo.SetActive(network != null && network.IsHost());
				nameInputFieldGo.SetActive(false);
				confirmButtonGo.SetActive(false);
				resignButtonGo.SetActive(false);
				restartButtonGo.SetActive(false);
				blackPlayerText.text = "";
				whitePlayerText.text = "";
				// 主机手动关闭，客机超时自动失败
				if (network != null && network.IsHost())
				{
					tipText.text = "等待连接。。。";
					if (hostStartToggle)
					{
						tipText.text = "";
						// 服务器连接好，自动猜先后发送给连接方
						self = (Player)Random.Range(1, 3);
						network.SendMessage("yourcolor," + ((int)self.other()).ToString());
						GameStart();
						hostStartToggle = false;
						curState = State.Playing;
					}
				}
				else
				{
					connectWatingTime += Time.deltaTime;
					tipText.text = $"寻找主机 {(int)connectWatingTime}秒";
					if (connectWatingTime > 10f)
					{
						// 大于10秒，连接失败
						ShowTip("连接超时");
						broadcastClient.Close();
						broadcastClient = null;
						if (network != null)
						{
							network.Close();
							network = null;
						}
						curState = State.NotConnect;
					}
				}
				
				break;
			case State.Playing:
				stopConnectButtonGo.SetActive(false);
				if (network.IsConnected())
				{
					restartButtonGo.SetActive(false);
					if (!game.IsOver)
					{
						InputCheck();

						blackPlayerText.text = self == Player.black ? selfName : opponentName;
						whitePlayerText.text = self == Player.black ? opponentName : selfName;
						blackImage.enabled = game.curGameState.next_player == Player.black;
						whiteImage.enabled = game.curGameState.next_player == Player.white;

						lastMoveHighLlight.position = new Vector3(-10, -10, -10);
						Move lastMove = game.curGameState.last_move;
						if (lastMove != null && lastMove.is_play)
						{
							if (game.IsVisiable(lastMove.point.row, lastMove.point.col))
							{
								lastMoveHighLlight.position = new Vector3(lastMove.point.row, lastMove.point.col, -0.03f);
							}
						}

						confirmButtonGo.SetActive(candidateMove != null);
						resignButtonGo.SetActive(game.IsSelfTurn);
						if (displayToggle)
						{
							UpdateDisplay();
							displayToggle = false;
						}
					}
					else
					{
						UpdateDisplay(true);
						curState = State.Gameover;
					}
				}
				else
				{
					network.Close();
					network = null;
					curState = State.NotConnect;
				}
				break;
			case State.Gameover:
				lastMoveHighLlight.position = new Vector3(-10, -10, -10);
				confirmButtonGo.SetActive(false);
				resignButtonGo.SetActive(false);
				restartButtonGo.SetActive(network.IsHost());
				break;
		}
		if (clearTipToggle)
		{
			tipText.text = "";
			clearTipToggle = false;
		}
	}

	void InputCheck()
	{
		if (Input.GetMouseButtonDown(0) && game != null && !game.IsOver && game.IsSelfTurn)
		{
			//从摄像机发出到点击坐标的射线
			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
			if (Physics.Raycast(ray, out RaycastHit hitInfo))
			{
				Vector3 hitPoint = hitInfo.point;
				int row = Mathf.RoundToInt(hitPoint.x);
				int col = Mathf.RoundToInt(hitPoint.y);

				//Debug.Log($"{row} {col}");

				Point point = new Point(row, col);
				ClearCandidate();
				if (game.CanTakeMove(point))
				{
					candidateMove = new Move(point);
					var sr = stoneRenders[candidateMove.point.row - 1, candidateMove.point.col - 1];
					sr.sprite = self == Player.black ? blackStoneSprite : whiteStoneSprite;
					sr.size = new Vector2(1, 1);
					sr.color = new Color(1, 1, 1, 0.7f);
				}
			}
		}
	}

	void ClearCandidate()
	{
		if (candidateMove != null)
		{
			var previourSr = stoneRenders[candidateMove.point.row - 1, candidateMove.point.col - 1];
			previourSr.sprite = null;
			previourSr.color = new Color(1, 1, 1, 1);
			candidateMove = null;
		}
	}

	void InitDisplay()
	{
		//UltiDraw.SetDepthRendering(true);
		Application.targetFrameRate = 30;

		stoneRenders = new SpriteRenderer[board_size, board_size];
		fogRenders = new SpriteRenderer[board_size, board_size];
		Transform stones = new GameObject("stones").transform;
		Transform fogs = new GameObject("fogs").transform;
		for (int i = 0; i < board_size; i++)
		{
			for (int j = 0; j < board_size; j++)
			{
				GameObject stone = new GameObject($"stone{i + 1} {j + 1}");
				var ssr = stone.AddComponent<SpriteRenderer>();
				ssr.sprite = null;
				ssr.drawMode = SpriteDrawMode.Sliced;
				ssr.size = new Vector2(stone_size, stone_size);
				stoneRenders[i, j] = ssr;
				stone.transform.position = new Vector3(i + 1, j + 1, -0.03f);
				stone.transform.parent = stones;

				GameObject fog = new GameObject($"fog{i + 1} {j + 1}");
				var fsr = fog.AddComponent<SpriteRenderer>();
				fsr.sprite = fogSprite;
				fsr.color = new Color(1, 1, 1, fogThick);
				fsr.drawMode = SpriteDrawMode.Sliced;
				fsr.size = new Vector2(1, 1);
				fogRenders[i, j] = fsr;
				fog.transform.position = new Vector3(i + 1, j + 1, -0.01f);
				fog.transform.parent = fogs;
			}
		}
	}

	void UpdateDisplay(bool noFog = false)
	{
		for (int i = 0; i < board_size; i++)
		{
			for (int j = 0; j < board_size; j++)
			{
				if (game.IsVisiable(i + 1, j + 1) || noFog)
				{
					Player color = game.GetColor(i + 1, j + 1);
					if (color == Player.black) stoneRenders[i, j].sprite = blackStoneSprite;
					else if (color == Player.white) stoneRenders[i, j].sprite = whiteStoneSprite;
					else stoneRenders[i, j].sprite = null;
					stoneRenders[i, j].size = new Vector2(stone_size, stone_size);

					fogRenders[i, j].sprite = null;
				}
				else
				{
					fogRenders[i, j].sprite = fogSprite;
					fogRenders[i, j].size = new Vector2(1, 1);
					stoneRenders[i, j].sprite = null;
				}
			}
		}
	}

	// 确认落子的回调（按钮）
	void ConfirmMove()
	{
		if (candidateMove != null)
		{
			game.TakeMove(candidateMove);
			string msg = $"move,{candidateMove.point.row},{candidateMove.point.col}";
			network.SendMessage(msg);
			ClearCandidate();
			displayToggle = true;
		}
	}

	void Resign()
	{
		if (game != null && !game.IsOver && game.IsSelfTurn)
		{
			game.TakeMove(Move.resign);
			network.SendMessage("resign");
		}
	}
	
	public void GameStart()
	{
		game = new FogGo(board_size, self);
		displayToggle = true;
		curState = State.Playing;
	}

	public void RestartGame()
	{
		if (game != null && game.IsOver && network != null && network.IsConnected() && network.IsHost())
		{
			self = (Player)Random.Range(1, 3);
			network.SendMessage("yourcolor," + ((int)self.other()).ToString());
			GameStart();
		}
	}

	// 网络消息接收回调
	void OnMessageReceive(string msg)
	{
		//ShowTip("收到消息: " + msg);
		string[] split = msg.Split(',');
		if (split[0] == "move")
		{
			Point point = new Point(int.Parse(split[1]), int.Parse(split[2]));
			game.TakeMove(new Move(point));
			displayToggle = true;
		}
		else if (split[0] == "pass")
		{
			game.TakeMove(Move.pass_turn);
		}
		else if (split[0] == "resign")
		{
			game.TakeMove(Move.resign);
		}
		else if (split[0] == "yourcolor")
		{
			// 只有客户端会收到这条消息
			self = (Player)int.Parse(split[1]);
			GameStart();
		}
		else if (split[0] == "myname")
		{
			// 只有服务器会收到这条消息
			opponentName = split[1];
		}
	}

	// 作为主机开始udp广播ip
	void SetAsHost()
	{
		selfName = nameInputField.text;
		if (string.IsNullOrEmpty(selfName)) selfName = "无名#" + Random.Range(1000, 9999);

		string data = $"fogGo,{localIp},{selfName}";
		broadcastServer = new BroadcastServer(port);
		broadcastServer.StartBroadcasting(data);
		
		network = new GameServer(localIp, port);
		network.StartConnect();
		network.RegistConnectedCallback(OnConnected);
		network.RegistReceiveCallback(OnMessageReceive);

		//serverButton.gameObject.SetActive(false);
		//clientButton.gameObject.SetActive(false);

		//ShowTip("开始广播");

		curState = State.WaitingConnect;
	}

	void EndBroadcast()
	{
		if (broadcastServer.broadcasting)
		{
			network?.Close();
			network = null;
			broadcastServer.Close();
			broadcastServer = null;
			curState = State.NotConnect;
		}
	}

	// 作为客机开始监听udp广播
	void FindingHost()
	{
		selfName = nameInputField.text;
		if (string.IsNullOrEmpty(selfName)) selfName = "无名#" + Random.Range(1000, 9999);

		broadcastClient = new BroadcastClient(port, ReceiveBroadcastCallback);
		broadcastClient.StartListening();

		//ShowTip("开始监听UDP广播");

		//serverButton.gameObject.SetActive(false);
		//clientButton.gameObject.SetActive(false);

		curState = State.WaitingConnect;
	}

	// 作为客机结束监听广播
	void StopFindingHost()
	{
		if (broadcastClient.listening)
		{
			network?.Close();
			network = null;
			broadcastClient.Close();
		}
	}

	// 作为客机接收到udp广播的回调
	void ReceiveBroadcastCallback(string data)
	{
		if (data.StartsWith("fogGo"))
		{
			//ShowTip("收到广播");
			string[] split = data.Split(',');
			opponentName = split[2];
			network = new GameClient(split[1], port);
			network.RegistConnectedCallback(OnConnected);
			network.RegistReceiveCallback(OnMessageReceive);
			network.StartConnect();
		}
	}

	void OnConnected()
	{
		clearTipToggle = true;
		if (network.IsHost())
		{
			//ShowTip("服务器连接");
			broadcastServer.Close();
			broadcastServer = null;

			hostStartToggle = true;
		}
		else
		{
			//ShowTip("客户端连接");
			broadcastClient.Close();
			broadcastClient = null;
			network.SendMessage("myname," + selfName);
		}
	}

	Coroutine tipCoroutine;
	bool showingTip = false;
	void ShowTip(string tip)
	{
		if (tipCoroutine != null)
		{
			StopCoroutine(tipCoroutine);
		}
		tipCoroutine = StartCoroutine(ShowTipCoroutine(tip));
	}

	IEnumerator ShowTipCoroutine(string tip)
	{
		showingTip = true;
		tipText.text = tip;
		yield return new WaitForSeconds(5);
		tipText.text = "";
		showingTip = false;
	}
}
